---
title: "Parameterization: compile-time metaprogramming"
description: An introduction to parameters and compile-time metaprogramming.
---

Many languages have facilities for *metaprogramming*: that is, for writing code that
generates or modifies code. Python has facilities for dynamic metaprogramming: features
like decorators, metaclasses, and many more. These features make Python very flexible
and productive, but since they're dynamic, they come with runtime overhead. Other
languages have static or compile-time metaprogramming features, like C preprocessor
macros and C++ templates. These can be limiting and hard to use.

To support Modular's work in AI, Mojo aims to provide powerful, easy-to-use
metaprogramming with zero runtime cost. This compile-time metaprogramming uses the same
language as runtime programs, so you don't have to learn a new language—just a few new
features.

The main new feature is *parameters*. You can think of a parameter as a
compile-time variable that becomes a runtime constant. This usage of "parameter"
is probably different from what you're used to from other languages, where
"parameter" and "argument" are often used interchangeably. In Mojo, "parameter"
and "parameter expression" refer to compile-time values, and "argument" and
"expression" refer to runtime values.

In Mojo, you can add parameters to a struct or function. You can also define
named parameter expressions—`comptime` values—that you can use as runtime
constants.

## Parameterized functions

To define a *parameterized function*, add parameters in square brackets ahead
of the argument list. Each parameter is formatted just like an argument: a
parameter name, followed by a colon and a type (which is required). In the
following example, the function has a single parameter, `count` of type `Int`.

```mojo
fn repeat[count: Int](msg: String):
    @parameter
    for i in range(count):
        print(msg)
```

The [`@parameter`](/mojo/manual/decorators/parameter) decorator shown here
causes the `for` loop to be evaluated at compile time. The decorator only works
if the loop limits are compile-time constants. Since `count` is a parameter,
`range(count)` can be calculated at compile time.

Calling a parameterized function, you provide values for the parameters, just
like function arguments:

```mojo
repeat[3]("Hello")
```

```output
Hello
Hello
Hello
```

The compiler resolves the parameter values during compilation, and creates a
concrete version of the `repeat[]()` function for each unique parameter value.
After resolving the parameter values and unrolling the loop, the `repeat[3]()`
function would be roughly equivalent to this:

```mojo
fn repeat_3(msg: String):
    print(msg)
    print(msg)
    print(msg)
```

:::note

This doesn't represent actual code generated by the compiler. By the
time parameters are resolved, Mojo code has already been transformed to an
intermediate representation in [MLIR](https://mlir.llvm.org/).

:::

If the compiler can't resolve all parameter values to constant values,
compilation fails.

## Anatomy of a parameter list

Parameters to a function or struct appear in square brackets after a function
or struct name. Parameters always require type annotations.

When you're looking at a function or struct definition, you may
see some special characters such as `/` and `*` in the parameter list.
Here's an example:

```mojo
def my_sort[
    # infer-only parameters
    dtype: DType,
    width: Int,
    //,
    # positional-only parameter
    values: SIMD[dtype, width],
    /,
    # positional-or-keyword parameter
    compare: fn (Scalar[dtype], Scalar[dtype]) -> Int,
    *,
    # keyword-only parameter
    reverse: Bool = False,
]() -> SIMD[dtype, width]:
```

Here's a quick overview of the special characters in the parameter list:

- Double slash (`//`): parameters declared before the double slash are
  [infer-only parameters](#infer-only-parameters).
- Slash (`/`): parameters declared before a slash are positional-only parameters. Positional-only
  and keyword-only parameters follow the same rules as
  [positional-only and keyword-only
  arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).
- A parameter name prefixed with a star, like `*Types` identifies a
  [variadic parameter](#variadic-parameters) (not shown in the example above).
  Any parameters following the variadic parameter are keyword-only.
- Star (`*`): in a parameter list with no variadic parameter, a star by itself
  indicates that the following parameters are keyword-only parameters.
- An equals sign (`=`) introduces a default value for an
  [optional parameter](#optional-parameters-and-keyword-parameters).

## Parameters and generics

"Generics" refers to functions that can act on multiple types of values, or
containers that can hold multiple types of values. For example,
[`List`](/mojo/stdlib/collections/list/List), can hold
different types of values, so you can have a list of `Int` values, or
a list of `String` values).

In Mojo, generics use parameters to specify types. For example, `List`
takes a type parameter, so a vector of integers is written `List[Int]`.
So all generics use parameters, but **not** everything that uses parameters is a
generic.

For example, the `repeat[]()` function in the previous section includes
parameter of type `Int`, and an argument of type `String`. It's parameterized,
but not generic. A generic function or struct is parameterized on *type*. For
example, we could rewrite `repeat[]()` to take any type of argument that
conforms to the [`Stringable`](/mojo/stdlib/builtin/str/Stringable) trait:

```mojo
fn repeat[MsgType: Stringable, //, count: Int](msg: MsgType):
    @parameter
    for i in range(count):
        print(String(msg))


def main():
    # MsgType is always inferred, so first positional keyword `2` is
    # passed to `count`
    repeat[2](42)
```

```output
42
42
```

This updated function takes any `Stringable` type, so you can pass it an `Int`,
`String`, or `Bool` value.

Note that there's a double-slash (`//`) in the parameter list after `MsgType`,
to specify that it's an [infer-only parameter](#infer-only-parameters), so you
don't need to specify it explicitly. Instead, the compiler sees that the `msg`
argument is an `Int` and *infers* the type from the value.

Mojo's support for generics is still early. You can write generic functions like
this using traits and parameters. You can also write generic collections like
`List` and `Dict`. If you're interested in learning how these types work, you
can find the source code for the standard library collection types
[on GitHub](https://github.com/modular/modular/blob/main/mojo/stdlib/stdlib/collections/).

## Parameterized structs

You can also add parameters to structs. You can use parameterized structs to
build generic collections. For example, a generic array type might include code
like this:

```mojo
struct GenericArray[ElementType: Copyable]:
    var data: UnsafePointer[Self.ElementType, MutOrigin.external]
    var size: Int

    fn __init__(out self, var *elements: Self.ElementType):
        self.size = len(elements)
        self.data = alloc[Self.ElementType](self.size)
        for i in range(self.size):
            (self.data + i).init_pointee_move(elements[i].copy())

    fn __del__(deinit self):
        for i in range(self.size):
            (self.data + i).destroy_pointee()
        self.data.free()

    fn __getitem__(self, i: Int) raises -> ref [self] Self.ElementType:
        if i < self.size:
            return self.data[i]
        else:
            raise Error("Out of bounds")
```

This struct has a single parameter, `ElementType`, which is a placeholder for
the data type you want to store in the array, sometimes called a *type
parameter*. `ElementType` conforms to the
[`Copyable`](/mojo/stdlib/builtin/value/Copyable) trait and therefore to the 
[`Movable`](/mojo/stdlib/builtin/value/Movable) trait.

As with parameterized functions, you need to pass in parameter values when you
use a parameterized struct. In this case, when you create an instance of
`GenericArray`, you need to specify the type you want to store, like `Int`, or
`Float64`. (This is a little confusing, because the *parameter value* you're
passing in this case is a *type*. That's OK: a Mojo type is a valid compile-time
value.)

You'll see that `ElementType` is used throughout the struct where you'd usually
see a type name. For example, as the formal type for the `elements` in the
constructor, and the return type of the `__getitem__()` method.

Here's an example of using `GenericArray`:

```mojo
var array = GenericArray(1, 2, 3, 4)
for i in range(array.size):
    end = ", " if i < array.size - 1 else "\n"
    print(array[i], end=end)
```

```output
1, 2, 3, 4
```

A parameterized struct can use the `Self` type to represent a concrete instance
of the struct (that is, with all its parameters specified). For example, you
could add a static factory method to `GenericArray` with the following
signature:

```mojo
struct GenericArray[ElementType: Copyable]:
    ...

    @staticmethod
    fn splat(count: Int, value: Self.ElementType) -> Self:
        # Create a new array with count instances of the given value
```

Here, `Self` is equivalent to writing `GenericArray[Self.ElementType]`. That is,
you can call the `splat()` method like this:

```mojo
GenericArray[Float64].splat(8, 0)
```

The method returns an instance of `GenericArray[Float64]`.

### Conditional conformance

When creating a generic struct, you might want to define some methods that
require extra features. For example, consider a collection like `GenericArray`
that holds instances of a type that conforms to the
[`Copyable`](/mojo/stdlib/builtin/value/Copyable) trait and therefore to the 
[`Movable`](/mojo/stdlib/builtin/value/Movable) trait. This imposes a lot
of limitations: you can't implement a `sort()` method because you can't
guarantee that the stored type supports the comparison operators; you can't
write a useful `__str__()` or `__repr__()` dunder method because you can't
guarantee that the stored type supports conversion to a string.

The answer to these issues is *conditional conformance*, which lets you define a
method that requires additional features. You do this by defining the `self`
value that has a more specific bound on one or more of its parameters.

For example, the following code defines a `Container` type that holds an
instance of a type conforming to `Movable`. It also defines a
`__str__()` method that can only be called if the stored `ElementType` conforms
to `Writable` as well as `Movable`:

```mojo
@fieldwise_init
struct Container[ElementType: Movable]:
    var element: Self.ElementType

    def __str__[
        StrElementType: Writable & Movable, //
    ](self: Container[StrElementType]) -> String:
        return String(self.element)


def main():
    float_container = Container(5.0)
    string_container = Container("Hello")
    print(float_container.__str__())
    print(string_container.__str__())
```

```output
5.0
Hello
```

Note the signature of the `__str__()` method, which declares the `self` argument
with a more specific type. Specifically, it declares that it takes a `Container`
with an `ElementType` that conforms to the `Writable` and `Movable`
traits.

```mojo
def __str__[StrElementType: Writable & Movable, //](
        self: Container[StrElementType]) -> String:
```

The trait bound on `StrElementType` must include `ElementType`'s original trait
(`Movable` in this case), either by composition or by inheritance. The trait
composition `Writable & Movable` includes the original trait. You could also use
a trait that inherits from `Movable`.

Note that the `main()` function calls the `__str__()` method directly,
rather than calling `String(float_container)`. One current limitation of
conditional conformance is that Mojo can't recognize the struct
`Container[Int]` as conforming to `Stringable`, even though the `__str__()`
method is implemented for any `ElementType` that's also `Stringable`.

### Case study: the SIMD type

For a real-world example of a parameterized type, let's look at the
[`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type from Mojo's standard library.

[Single instruction, multiple data (SIMD)](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) is a parallel processing technology built into many modern CPUs,
GPUs, and custom accelerators. SIMD allows you to perform a single operation on
multiple pieces of data at once. For example, if you want to take the square
root of each element in an array, you can use SIMD to parallelize the work.

Processors implement SIMD using low-level vector registers in hardware that hold
multiple instances of a scalar data type. In order to use the SIMD instructions
on these processors, the data must be shaped into the proper SIMD width
(data type) and length (vector size). Processors may support 512-bit or
longer SIMD vectors, and support many data types from 8-bit integers to 64-bit
floating point numbers, so it's not practical to define all of the possible SIMD
variations.

Mojo's [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type (defined as a struct)
exposes the common SIMD operations through its methods, and makes the SIMD data
type and size values parametric. This allows you to directly map your data to
the SIMD vectors on any hardware.

Here's a cut-down (non-functional) version of Mojo's `SIMD` type definition:

```mojo
struct SIMD[type: DType, size: Int]:
    var value: … # Some low-level MLIR stuff here

    # Create a new SIMD from a number of scalars
    fn __init__(out self, *elems: SIMD[Self.type, 1]):  ...

    # Fill a SIMD with a duplicated scalar value.
    @staticmethod
    fn splat(x: SIMD[Self.type, 1]) -> SIMD[Self.type, Self.size]: ...

    # Cast the elements of the SIMD to a different elt type.
    fn cast[target: DType](self) -> SIMD[target, Self.size]: ...

    # Many standard operators are supported.
    fn __add__(self, rhs: Self) -> Self: ...
```

So you can create and use a SIMD vector like this:

```mojo
var vector = SIMD[DType.int16, 4](1, 2, 3, 4)
vector = vector * vector
for i in range(4):
    print(vector[i], end=" ")
```

```output
1 4 9 16
```

As you can see, a simple arithmetic operator like `*` applied to a pair of
`SIMD` vector operates on the corresponding elements in each vector.

Defining each SIMD variant with parameters is great for code reuse because the
`SIMD` type can express all the different vector variants statically, instead of
requiring the language to pre-define every variant.

Because `SIMD` is a parameterized type, the `self` argument in its functions
carries those parameters—the full type name is `SIMD[type, size]`. Although
it's valid to write this out (as shown in the return type of `splat()`), this
can be verbose, so we recommend using the `Self` type (from
[PEP673](https://peps.python.org/pep-0673/)) like the `__add__` example does.

## Overloading on parameters

Functions and methods can be overloaded on their parameter signatures. For
information on overload resolution, see
[Overloaded functions](/mojo/manual/functions#overloaded-functions).

## Using parameterized types and functions

You can use parametric types and functions by passing values to the
parameters in square brackets. For example, for the `SIMD` type above, `type`
specifies the data type and `size` specifies the length of the SIMD vector (it
must be a power of 2):

```mojo
def main():
    # Make a vector of 4 floats.
    var small_vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0)

    # Make a big vector containing 1.0 in float16 format.
    var big_vec = SIMD[DType.float16, 32](1.0)

    # Do some math and convert the elements to float32.
    var bigger_vec = (big_vec + big_vec).cast[DType.float32]()

    # You can write types out explicitly if you want of course.
    var bigger_vec2: SIMD[DType.float32, 32] = bigger_vec

    print("small_vec DType:", small_vec.dtype, "size:", small_vec.size)
    print(
        "bigger_vec2 DType:",
        bigger_vec2.dtype,
        "size:",
        bigger_vec2.size,
    )
```

```output
small_vec type: float32 length: 4
bigger_vec2 type: float32 length: 32
```

Note that the `cast()` method also needs a parameter to specify the type you
want from the cast (the method definition above expects a `target` parametric
value). Thus, just as the `SIMD` struct is a generic type definition, the
`cast()` method is a generic method definition. At compile time, the compiler
creates a concrete version of the `cast()` method with the target parameter
bound to `DType.float32`.

The code above shows the use of concrete types (that is, the parameters are all
bound to known values). But the major power of parameters comes from the
ability to define parametric algorithms and types (code that uses the parameter
values). For example, here's how to define a parametric algorithm with `Scalar`
that is datatype agnostic:

```mojo
from math import sqrt

fn rsqrt[dt: DType](x: Scalar[dt]) -> Scalar[dt]:
    return 1 / sqrt(x)

def main():
    var v = Scalar[DType.float16](42)
    print(rsqrt(v))
```

```output
0.154296875
```

Notice that the `x` argument is a `Scalar` type that's parameterized
based on the `dt` function parameter. And note that the actual call to `rsqrt()`
doesn't specify the `dt` parameter, because it can be _inferred_ from the
argument's type (here, `Scalar[DType.float16]`).

### Parameter inference

The Mojo compiler can often *infer* parameter values, so you don't always have
to specify them. For example, you can call the `rsqrt()` function defined above
without specifying the `dt` parameter:

```mojo
from math import sqrt

fn rsqrt[dt: DType](x: Scalar[dt]) -> Scalar[dt]:
    return 1 / sqrt(x)

def main():
    var v = Scalar[DType.float16](33)
    print(rsqrt(v))
```

```output
0.174072265625
```

The compiler infers the `dt` parameter based on the parametric `v`
value passed into it, as if you wrote `rsqrt[DType.float16](v)` explicitly.
Figure 1 shows a mental model for how parameter inference works.

<figure>

![](../images/parameters/parameter-inference.png#light)
![](../images/parameters/parameter-inference-dark.png#dark)

<figcaption><b>Figure 1.</b> Parameter inference</figcaption>
</figure>

Parameter inference can seem a little confusing: it might seem like the compiler
is inferring compile-time parameter values from run-time argument values. But in
fact it's inferring parameters from the statically-known *types* of the
arguments.

:::note Inference failures

If parameter inference fails, the compiler will report an error, usually "failed
to infer parameter 'param_name'". Unfortunately, the compiler also sometimes
reports this error incorrectly, for example, when the actual error is a type
mismatch. In these cases, specifying the missing parameters explicitly will
often allow Mojo to report the correct error.

:::

Mojo can also infer the values of struct parameters from the arguments passed to
a constructor or static method.

For example, consider the following struct:

```mojo
struct One[Type: Writable & Copyable]:
    var value: Self.Type

    fn __init__(out self, value: Self.Type):
        self.value = value.copy()


def use_one():
    s1 = One(123)  # equivalent to One[Int](123)
    s2 = One("Hello")  # equivalent to One[String]("Hello")
```

Note that you can create an instance of `One` without specifying the `Type`
parameter—Mojo can infer it from the `value` argument.

You can also infer parameters from a parameterized type passed to a constructor
or static method:

```mojo
struct Two[Type: Writable & Copyable]:
    var val1: Self.Type
    var val2: Self.Type

    fn __init__(out self, one: One[Self.Type], another: One[Self.Type]):
        self.val1 = one.value.copy()
        self.val2 = another.value.copy()
        print(String(self.val1), String(self.val2))

    @staticmethod
    fn fire(thing1: One[Self.Type], thing2: One[Self.Type]):
        print("🔥", String(thing1.value), String(thing2.value))

def use_two():
    s3 = Two(One("infer"), One("me"))
    Two.fire(One(1), One(2))
    # Two.fire(One("mixed"), One(0)) # Error: parameter inferred to two
                                     # different values

use_two()
```

```output
infer me
🔥 1 2
```

`Two` takes a `Type` parameter, and its constructor takes values of type
`One[Type]`. When constructing an instance of `Two`, you don't need to specify
the `Type` parameter, since it can be inferred from the arguments.

Similarly, the static `fire()` method takes values of type `One[Type]`, so Mojo
can infer the `Type` value at compile time. Note that passing two instances of
`One` with different types doesn't work.

:::note

If you're familiar with C++, you may recognize this as similar to Class Template
Argument Deduction (CTAD).

:::

## Optional parameters and keyword parameters

Just as you can specify [optional
arguments](/mojo/manual/functions#optional-arguments) in function signatures,
you can also define an optional *parameter* by giving it a default value.

You can also pass parameters by keyword, just like you can use
[keyword arguments](/mojo/manual/functions#keyword-arguments).
For a function or struct with multiple optional parameters, using keywords
allows you to pass only the parameters you want to specify, regardless of
their position in the function signature.

For example, here's a function with two parameters, each with a default value:

```mojo
fn speak[a: Int = 3, msg: String = "woof"]():
    print(msg, a)


fn use_defaults():
    speak()  # prints 'woof 3'
    speak[5]()  # prints 'woof 5'
    speak[7, "meow"]()  # prints 'meow 7'
    speak[msg="baaa"]()  # prints 'baaa 3'
```

Recall that when a parametric function is called, Mojo can
[infer the parameter values](#parameter-inference). That is, it can determine
its parameter values from the parameters attached to an argument. If the
parametric function also has a default value defined, then the inferred
parameter value takes precedence.

For example, in the following code, we update the parametric `speak[]()` function
to take an argument with a parametric type. Although the function has a default
parameter value for `a`, Mojo instead uses the inferred `a` parameter value
from the `bar` argument (as written, the default `a` value can never be used,
but this is just for demonstration purposes):

```mojo
@fieldwise_init
struct Bar[v: Int]:
    pass


fn speak[a: Int = 3, msg: String = "woof"](bar: Bar[a]):
    print(msg, a)


fn use_inferred():
    speak(Bar[9]())  # prints 'woof 9'
```

As mentioned above, you can also use optional parameters and keyword
parameters in a struct:

```mojo
struct KwParamStruct[greeting: String = "Hello", name: String = "🔥mojo🔥"]:
    fn __init__(out self):
        print(Self.greeting, Self.name)

fn use_kw_params():
    var a = KwParamStruct[]()                 # prints 'Hello 🔥mojo🔥'
    var b = KwParamStruct[name="World"]()     # prints 'Hello World'
    var c = KwParamStruct[greeting="Hola"]()  # prints 'Hola 🔥mojo🔥'
```

:::note

Mojo supports positional-only and keyword-only parameters, following the same
rules as [positional-only and keyword-only
arguments](/mojo/manual/functions#positional-only-and-keyword-only-arguments).

:::

## Infer-only parameters

Sometimes you need to declare functions where parameters depend on other
parameters. Because the signature is processed left to right, a parameter can
only *depend* on a parameter earlier in the parameter list. For example:

```mojo
fn dependent_type[dtype: DType, value: Scalar[dtype]]():
    print("Value: ", value)
    print("Value is floating-point: ", dtype.is_floating_point())

dependent_type[DType.float64, Float64(2.2)]()
```

```output
Value:  2.2000000000000002
Value is floating-point:  True
```

You can't reverse the position of the `dtype` and `value` parameters, because
`value` depends on `dtype`. However, because `dtype` is a required parameter,
you can't leave it out of the parameter list and let Mojo infer it from `value`:

```mojo
dependent_type[Float64(2.2)]() # Error!
```

Infer-only parameters are a special class of parameters that are **always** either
inferred from context or specified by keyword. Infer-only parameters are placed at the
**beginning** of the parameter list, set off from other parameters by the `//` sigil:

```mojo
fn example[type: Copyable, //, list: List[type]]()
```

Transforming `dtype` into an infer-only parameter solves this problem:

```mojo
fn dependent_type[dtype: DType, //, value: Scalar[dtype]]():
    print("Value: ", value)
    print("Value is floating-point: ", dtype.is_floating_point())

dependent_type[Float64(2.2)]()
```

```output
Value:  2.2000000000000002
Value is floating-point:  True
```

Because infer-only parameters are declared at the beginning of the parameter
list, other parameters can depend on them, and the compiler will always attempt
to infer the infer-only values from bound parameters or arguments.

There are sometimes cases where it's useful to specify an infer-only parameter
by keyword. For example, the
[`Span`](/mojo/stdlib/memory/span/Span/) type
is parametric on [origin](/mojo/manual/values/lifetimes):

```mojo
struct Span[mut: Bool, //, T: Copyable, origin: Origin[mut], ...]:
    ...
```

Here, the `mut` parameter is infer-only. The value is usually inferred when you
create an instance of `Span`. Binding the `mut` parameter by keyword lets you
define a `Span` that requires a mutable origin.

```mojo
def mutate_span(span: Span[mut=True, Byte]):
    for i in range(0, len(span), 2):
        if i + 1 < len(span):
            span.swap_elements(i, i + 1)
```

If the compiler can't infer the value of an infer-only parameter, and it's not
specified by keyword, compilation fails.

## Variadic parameters

Mojo also supports variadic parameters, similar to
[Variadic arguments](/mojo/manual/functions#variadic-arguments):

```mojo
struct MyTensor[*dimensions: Int]:
    pass
```

Variadic parameters currently have some limitations that variadic arguments don't have:

* Variadic parameters must be homogeneous—that is, all the values must be the
  same type.

* The parameter type must be register-passable.

* The parameter values aren't automatically projected into a `VariadicList`, so you
  need to construct the list explicitly:

```mojo
fn sum_params[*values: Int]() -> Int:
    comptime list = VariadicList(values)
    var sum = 0
    for v in list:
        sum += v
    return sum
```

Variadic keyword parameters (for example, `**kwparams`) are
not supported yet.

## Parameter expressions are just Mojo code

A parameter expression is any code expression (such as `a+b`) that occurs where
a parameter is expected. Parameter expressions support operators and function
calls, just like runtime code, and all parameter types use the same type
system as the runtime program (such as `Int` and `DType`).

Because parameter expressions use the same grammar and types as runtime
Mojo code, you can use many
["dependent type"](https://en.wikipedia.org/wiki/Dependent_type) features. For
example, you might want to define a helper function to concatenate two SIMD
vectors:

```mojo
fn concat[
    dtype: DType, ls_size: Int, rh_size: Int, //
](lhs: SIMD[dtype, ls_size], rhs: SIMD[dtype, rh_size]) -> SIMD[
    dtype, ls_size + rh_size
]:
    var result = SIMD[dtype, ls_size + rh_size]()

    @parameter
    for i in range(ls_size):
        result[i] = lhs[i]

    @parameter
    for j in range(rh_size):
        result[ls_size + j] = rhs[j]
    return result
```

```output
result type: float32 length: 4
```

Note that the resulting length is the sum of the input vector lengths, and this
is expressed with a simple `+` operation.

### Powerful compile-time programming

While simple expressions are useful, sometimes you want to write imperative
compile-time logic with control flow. You can even do compile-time recursion.
For instance, here is an example "tree reduction" algorithm that sums all
elements of a vector recursively into a scalar:

```mojo
fn slice[
    dtype: DType, size: Int, //
](x: SIMD[dtype, size], offset: Int) -> SIMD[dtype, size // 2]:
    comptime new_size = size // 2
    var result = SIMD[dtype, new_size]()
    for i in range(new_size):
        result[i] = SIMD[dtype, 1](x[i + offset])
    return result


fn reduce_add(x: SIMD) -> Int:
    @parameter
    if x.size == 1:
        return Int(x[0])
    elif x.size == 2:
        return Int(x[0]) + Int(x[1])

    # Extract the top/bottom halves, add them, sum the elements.
    comptime half_size = x.size // 2
    var lhs = slice(x, 0)
    var rhs = slice(x, half_size)
    return reduce_add(lhs + rhs)


def main():
    var x = SIMD[DType.int, 4](1, 2, 3, 4)
    print(x)
    print("Elements sum:", reduce_add(x))
```

```output
[1, 2, 3, 4]
Elements sum: 10
```

This makes use of the [`@parameter`](/mojo/manual/decorators/parameter) decorator to
create a parametric if condition, which is an `if` statement that runs at compile-time.
It requires that its condition be a valid parameter expression, and ensures that only
the live branch of the `if` statement is compiled into the program. (This is similar to
use of the `@parameter` decorator with a `for` loop shown earlier.)

## `comptime`: named parameter expressions

It is very common to want to *name* compile-time values. Whereas `var` defines a
runtime value, we need a way to define a
compile-time temporary value. For this, Mojo uses a `comptime` declaration. At its
simplest, `comptime` can be used to define a constant value:

```mojo
comptime rows = 512
```

A `comptime` value is always evaluated at compile time, so you can use
`comptime` to force a function to run at compile time. You can use this to
calculate constant values based on information available at compile time, such
as hardware parameters.

```mojo
comptime block_size = _calculate_block_size()
```

Types are another common use for `comptime` values. Because types are
compile-time expressions, you can use a `comptime` value as a shorthand (a type
alias or "typedef") for a parameterized type:

```mojo
comptime Float16 = SIMD[DType.float16, 1]
comptime UInt8 = SIMD[DType.uint8, 1]

var x: Float16 = 0  # Float16 works like a "typedef"
```

(These aliases and others are actually defined in the
[`simd` module](/mojo/stdlib/builtin/simd/#comptime-values).)

Like `var` variables, `comptime` values obey scope, and you can use local
`comptime` values within functions as you'd expect.

### Parametric `comptime` values

A *parametric `comptime` value* is a compile-time expression that takes a
list of parameters and returns a compile-time constant value:

```mojo
comptime AddOne[a: Int] : Int = a + 1

comptime nine = AddOne[8]
```

As you can see in the previous example, a parametric `comptime` value is a
little like a *compile-time-only function*. A regular function or method can
also be invoked at compile time:

```mojo
fn add_one(a: Int) -> Int:
    return a + 1

comptime ten = add_one(9)
```

A major difference between a function and a parametric `comptime` value is that
the value of a `comptime` expression can be a type, while a function can't
return a type as a value.

```mojo
# Does not work—-dynamic type values not permitted
fn int_type() -> AnyType:
    return Int

# Works
comptime IntType = Int
```

Because a `comptime` value can be a type, you can use parametric `comptime`
values to express new types:

```mojo
comptime TwoOfAKind[dt: DType] = SIMD[dt, 2]
twoFloats = TwoOfAKind[DType.float32](1.0, 2.0)

comptime StringKeyDict[ValueType: Copyable] = Dict[String, ValueType]
var b: StringKeyDict[UInt8] = {"answer": 42}
```

Parametric `comptime` declarations support the same features as parameterized
structs or functions: infer-only parameters, keyword-only and optional
parameters, [automatic parameterization](#automatic-parameterization), and so
on.

```mojo
    comptime Floats[size: Int, half_width: Bool = False] = SIMD[
        (DType.float16 if half_width else DType.float32), size
    ]
    var floats = Floats[2](6.0, 8.0)
    var half_floats = Floats[2, True](10.0, 12.0)
```

### Comptime members

You can also define `comptime` values as members of a `struct` definition.
These `comptime` members have a number of uses:

- Constant values specific to the type.
- Constant values calculated based on the struct's parameters.
- Derived types based on the struct's parameters.

Derived types are a common use for `comptime` members. For example, a `List[T]`
struct holds values of type `T`. The list's `__iter__()` method returns a list
iterator that returns values of type `T`. `List` uses a `comptime` member,
`IteratorType`, to define the type of the returned iterator.

The following code excerpt shows a simplified version of some of the `List`
code, showing the `List` and its derived `IteratorType`:

```mojo
@fieldwise_init
struct _ListIter[
    mut: Bool, //,
    T: Copyable,
    origin: Origin[mut],
](ImplicitlyCopyable, Iterable, Iterator):

    comptime Element = Self.T  # Required by the Iterator trait

    var index: Int
    var src: Pointer[List[Self.Element], Self.origin]

    ...

struct List[T: Copyable](
    Boolable, Copyable, Defaultable, Iterable, Sized
):
    comptime IteratorType[
        iterable_mut: Bool, //, iterable_origin: Origin[iterable_mut]
    ]: Iterator = _ListIter[Self.T, iterable_origin]

    ...

    fn __iter__(ref self) -> Self.IteratorType[origin_of(self)]:
        return {0, Pointer(to=self)}
    ...
```

The `IteratorType` `comptime` member is parameterized on an origin, so it can
represent both mutable and immutable iterators.

Some Mojo types use `comptime` members to express enumerations. For example, the
following code defines a `Sentiment` type that defines `comptime` constants
for different sentiment values:

```mojo
@fieldwise_init
struct Sentiment(Equatable, ImplicitlyCopyable):
    var _value: Int

    comptime NEGATIVE = Sentiment(0)
    comptime NEUTRAL = Sentiment(1)
    comptime POSITIVE = Sentiment(2)

    fn __eq__(self, other: Self) -> Bool:
        return self._value == other._value

    fn __ne__(self, other: Self) -> Bool:
        return not (self == other)

fn is_happy(s: Sentiment):
    if s == Sentiment.POSITIVE:
        print("Yes. 😀")
    else:
        print("No. ☹️")
```

This pattern provides a type-safe enumeration.

The [`DType`](/mojo/stdlib/builtin/dtype/DType) struct implements a simple enum
using `comptime` members like this. This allows clients to use values like
`DType.float32` in parameter expressions or runtime expressions.

## Fully-bound, partially-bound, and unbound types

A parametric type with its parameters specified is said to be *fully-bound*.
That is, all of its parameters are bound to values. As mentioned before, you can
only instantiate a fully-bound type (sometimes called a *concrete type*).

However, parametric types can be *unbound* or *partially bound* in some
contexts. For example, you can use `comptime` to create a type alias to a
partially-bound type to create a new type that requires fewer parameters:

```mojo
comptime StringKeyDict = Dict[String, _]
var b: StringKeyDict[UInt8] = {"answer": 42}
```

Here, `StringKeyDict` is a type alias for a `Dict` that takes `String` keys. The
underscore `_` in the parameter list indicates that the second parameter,
`V` (the value type), is unbound.
You specify the `V` parameter later, when you use `StringKeyDict`.

:::note Partially-bound types versus parametric `comptime` values

You may notice that this example is very similar to an example in the section on
[parametric `comptime` values](#parametric-comptime-values). For simple type
aliases like this, you can use either a partially-bound type or a parametric
`comptime` value. Parametric `comptime` values provide a more flexible way to
define type aliases, since you can define the order of the parameters, add
default values, and so on.

Partially-bound and unbound types can provide a handy shortcut when defining
parametric functions and `comptime` values, called
[automatic parameterization](#automatic-parameterization).

:::

You can also use partially-bound types as the type bound for an argument or
parameter.

For example, given the following type:

```mojo
struct MyType[s: String, i: Int, i2: Int, b: Bool = True]:
    pass
```

It can appear in code in the following forms:

* *Fully bound*, with all of its parameters specified:

  ```mojo
  def my_fn1(m1: MyType["Hello", 3, 4, True]):
      pass
  ```

* *Partially bound*, with *some but not all* of its parameters specified:

  ```mojo
  def my_fn2(m2: MyType["Hola", _, _, True]):
      pass
  ```

* *Unbound*, with no parameters specified:

  ```mojo
  def my_fn3(m3: MyType[_, _, _, _]):
      pass
  ```

You can also use the star-underscore expression `*_` to unbind an arbitrary
number of positional parameters at the end of a parameter list:

```mojo
# These two types are equivalent
MyType["Hello", *_]
MyType["Hello", _, _, _]
```

The `*_` expression specifically matches any parameters that can be specified by
position (positional-only or positional-or-keyword). To unbind all parameters
that can be specified by keyword (positional-or-keyword or keyword-only), use
the double-star-underscore expression, `**_`.

```mojo
@fieldwise_init
struct KeyWordStruct[pos_or_kw: Int, *, kw_only: Int = 10]:
    pass

# Unbind both pos_or_kw and kw_only parameters
fn use_kw_struct(k: KeyWordStruct[**_]):
    pass

def main():
    use_kw_struct(KeyWordStruct[10, kw_only=11]())
```

When a parameter is explicitly unbound with the `_`, `*_`, or `**_` expressions,
you **must** specify a value for that parameter to use the type.
The default values of explicitly unbound parameters are ignored.

Partially-bound and unbound parametric types can be used in some contexts where
the missing (unbound) parameters will be supplied later—such as in
[`comptime` values](#comptime-named-parameter-expressions) and
[automatically parameterized functions](#automatic-parameterization).

### Omitted parameters

Mojo also supports an alternate format for unbound parameters where parameters
are simply omitted from the expression:

```mojo
@fieldwise_init
struct MyComplicatedType[a: Int = 7, /, b: Int = 8, *, c: Int, d: Int = 9]:
    pass

# Unbound
fn my_func(t: MyComplicatedType):
    pass
```

This is equivalent to `fn my_func(t: MyComplicatedType[*_, **_]): pass`.
That is, all parameters (positional-only, positional-or-keyword, keyword-only)
are unbound and their default values (if any) ignored.

Note that when an argument type is _partially bound_, default values will be
bound:

```mojo
# Partially bound
MyComplicatedType[1]
# Equivalent to
MyComplicatedType[1, 8, c=_, d=9]  # Uses default values for `b` and `d`.
```

:::note

This behavior with omitted parameters is currently supported for backwards
compatibility. We intend to reconcile the behavior of omitted parameters and
explicitly unbound parameters in the future.

:::

## Automatic parameterization

Mojo supports "automatic" parameterization of functions and parametric
`comptime` values. If a function argument type or parameter type is
[partially-bound or unbound](#fully-bound-partially-bound-and-unbound-types),
the unbound parameters are automatically added as parameters on the
function. This is easier to understand with an example:

```mojo
fn print_params(vec: SIMD):
    print(vec.type)
    print(vec.size)

var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)
print_params(v)
```

```output
float64
4
```

In the above example, the `print_params` function is automatically
parameterized. The `vec` argument takes an argument of type `SIMD[*_]`. This is
an [unbound parameterized
type](#fully-bound-partially-bound-and-unbound-types)—that is, it doesn't
specify any parameter values for the type. Mojo treats the unbound parameters
on `vec` as infer-only parameters on the function. This is roughly equivalent to
the following code:

```mojo
fn print_params2[t: DType, s: Int, //](vec: SIMD[t, s]):
    print(vec.dtype)
    print(vec.size)
```

When you call `print_params()` you must pass it a concrete instance of the
`SIMD`  type—that is, one with all of its parameters specified, like
`SIMD[DType.float64, 4]`. The Mojo compiler *infers* the parameter
values from the input argument.

With a manually parameterized function, you can access the parameters by
name (for example, `t` and `s` in the previous example). For an
automatically parameterized function, you can access the parameters as
attributes on the argument (for example, `vec.type`).

This ability to access a type's parameters is not specific to
automatically parameterized functions, you can use it anywhere. You can access
the parameters of a parameterized type as attributes on the type itself:

```mojo
fn on_type():
    print(SIMD[DType.float32, 2].size) # prints 2
```

Or as attributes on an *instance* of the type:

```mojo
fn on_instance():
    var x = SIMD[DType.int32, 2](4, 8)
    print(x.dtype)  # prints int32
```

You can even use this syntax in the function's signature to define a
function's arguments and return type based on an argument's parameters.
For example, if you want your function to take two SIMD vectors with the same
type and size, you can write code like this:

```mojo
fn interleave(v1: SIMD, v2: type_of(v1)) -> SIMD[v1.type, v1.size*2]:
    var result = SIMD[v1.type, v1.size*2]()
    for i in range(v1.size):
        result[i*2] = SIMD[v1.type, 1](v1[i])
        result[i*2+1] = SIMD[v1.type, 1](v2[i])
    return result

var a = SIMD[DType.int16, 4](1, 2, 3, 4)
var b = SIMD[DType.int16, 4](0, 0, 0, 0)
var c = interleave(a, b)
print(c)
```

```output
[1, 0, 2, 0, 3, 0, 4, 0]
```

As shown in the example, you can use the magic `type_of(x)` call if you just want to
match the type of an argument. In this case, it's more convenient and compact that
writing the equivalent `SIMD[v1.type, v1.size]`.

### Automatic parameterization of parameters

You can also take advantage of automatic parameterization in the parameter list
of a function or parametric `comptime` value. For example:

```mojo
fn foo[value: SIMD]():
    pass

# Equivalent to:
fn foo[dtype: DType, size: Int, //, value: SIMD[dtype, size]]():
    pass
```

Here's another example using a parametric `comptime` value:

```mojo
comptime Foo[S: SIMD] = Bar[S]

# Equivalent to:
comptime Foo[dtype: DType, size: Int, //, S: SIMD[dtype, size]] = Bar[S]
```

### Automatic parameterization with partially-bound types

Mojo also supports automatic parameterization: with [partially-bound
parameterized types](#fully-bound-partially-bound-and-unbound-types) (that is,
types with some but not all of the parameters specified).

For example, suppose we have a `Fudge` struct with three parameters:

```mojo
@fieldwise_init
struct Fudge[sugar: Int, cream: Int, chocolate: Int = 7](Stringable):
    fn __str__(self) -> String:
        return String.write(
            "Fudge (", Self.sugar, ",", Self.cream, ",", Self.chocolate, ")"
        )
```

We can write a function that takes a `Fudge` argument with just one bound
parameter (it's *partially bound*):

```mojo
fn eat(f: Fudge[5, *_]):
    print("Ate " + String(f))
```

The `eat()` function takes a `Fudge` struct with the first parameter (`sugar`)
bound to the value 5. The second and third parameters, `cream` and `chocolate`
are unbound.

The unbound `cream` and `chocolate` parameters become implicit parameters
on the `eat` function. In practice, this is roughly equivalent to writing:

```mojo
fn eat[cr: Int, ch: Int](f: Fudge[5, cr, ch]):
    print("Ate", String(f))
```

In both cases, we can call the function by passing in an instance with the
`cream` and `chocolate` parameters bound:

```mojo
eat(Fudge[5, 5, 7]())
eat(Fudge[5, 8, 9]())
```

```output
Ate Fudge (5,5,7)
Ate Fudge (5,8,9)
```

If you try to pass in an argument with a `sugar` value other than 5,
compilation fails, because it doesn't match the argument type:

```mojo
eat(Fudge[12, 5, 7]())
# ERROR: invalid call to 'eat': argument #0 cannot be converted from 'Fudge[12, 5, 7]' to 'Fudge[5, 5, 7]'
```

You can also explicitly unbind individual parameters. This gives you
more freedom in specifying unbound parameters.

For example, you might want to let the user specify values for `sugar` and
`chocolate`, and leave `cream` constant. To do this, replace each unbound
parameter value with a single underscore (`_`):

```mojo
fn devour(f: Fudge[_, 6, _]):
    print("Devoured",  String(f))
```

Again, the unbound parameters (`sugar` and `chocolate`) are added as implicit
parameters on the function. This version is roughly equivalent to the
following code, where these two values are explicitly bound to the input
parameters, `su` and `ch`:

```mojo
fn devour[su: Int, ch: Int](f: Fudge[su, 6, ch]):
    print("Devoured", String(f))
```

You can also specify parameters by keyword, or mix positional and keyword
parameters, so the following function is roughly equivalent to the previous one:
the first parameter, `sugar` is explicitly unbound with the underscore character.
The `chocolate` parameter is unbound using the keyword syntax, `chocolate=_`.
And `cream` is explicitly bound to the value 6:

```mojo
fn devour(f: Fudge[_, chocolate=_, cream=6]):
    print("Devoured", String(f))
```

All three versions of the `devour()` function work with the following calls:

```mojo
devour(Fudge[3, 6, 9]())
devour(Fudge[4, 6, 8]())
```

```output
Devoured Fudge (3,6,9)
Devoured Fudge (4,6,8)
```

### Legacy syntax (omitted parameters)

You can also specify an unbound or partially-bound type by omitting parameters:
for example:

```mojo
fn nibble(f: Fudge[5]):
    print("Ate", String(f))

nibble(Fudge[5, 4, 7]())

```

```output
Ate Fudge (5,4,7)
```

Here, `Fudge[5]` works like `Fudge[5, *_]` **except** in the handling of
parameters with default values. Instead of discarding the default value of
`chocolate`, `Fudge[5]` binds the default value immediately, making it
equivalent to: `Fudge[5, _, 7]`.

This means that the following code won't compile with the previous definition
for the `nibble()` function, since it doesn't use the default value for
`chocolate`:

```mojo
nibble(Fudge[5, 5, 9]())
# ERROR: invalid call to 'nibble': argument #0 cannot be converted from 'Fudge[5, 5, 9]' to 'Fudge[5, 5, 7]'
```

:::note TODO

We intend to reconcile the behavior of omitted parameters and explicitly unbound
parameters in the future.

:::

## The `rebind()` builtin

One of the consequences of Mojo not performing function instantiation in the
parser like C++ is that Mojo cannot always figure out whether some parametric
types are equal and complain about an invalid conversion. This typically occurs
in static dispatch patterns. For example, the following code won't compile:

```mojo
fn take_simd8(x: SIMD[DType.float32, 8]):
    pass

fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]):
    @parameter
    if nelts == 8:
        take_simd8(x)
```

The parser will complain:

```plaintext
error: invalid call to 'take_simd8': argument #0 cannot be converted from
'SIMD[f32, nelts]' to 'SIMD[f32, 8]'
        take_simd8(x)
        ~~~~~~~~~~^~~
```

This is because the parser fully type-checks the function without instantiation,
and the type of `x` is still `SIMD[f32, nelts]`, and not `SIMD[f32, 8]`, despite
the static conditional. The remedy is to manually "rebind" the type of `x`,
using the `rebind` builtin, which inserts a compile-time assert that the input
and result types resolve to the same type after function instantiation:

```mojo
fn take_simd8(x: SIMD[DType.float32, 8]):
    pass

fn generic_simd[nelts: Int](x: SIMD[DType.float32, nelts]):
    @parameter
    if nelts == 8:
        take_simd8(rebind[SIMD[DType.float32, 8]](x))
```
